package me.ingxin.android.keyboard.view

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Paint.Align
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import me.ingxin.android.keyboard.entry.Key
import me.ingxin.android.tools.dp2px
import kotlin.math.abs
import kotlin.math.max

/**
 * Created by ingxin on 2023/2/3
 * 键盘View
 */
abstract class BaseKeyboardView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr), IKeyboard {

    private val keyTextPaint = Paint()
    private val keyIcons = HashMap<Int, Drawable?>()

    protected var horizontalGap: Float
        private set

    protected var verticalGap: Float
        private set

    protected var themeColor = Color.TRANSPARENT
        private set

    private var keyBackground: Drawable? = null
    private var onKeyListener: IKeyboard.OnKeyListener? = null
    private var downKey: Key? = null
    protected var keyBuffer: List<Key>? = null

    override fun setOnKeyListener(listener: IKeyboard.OnKeyListener) {
        this.onKeyListener = listener
    }

    override fun setKeyIcon(keyCode: Int, drawable: Drawable?) {
        keyIcons[keyCode] = drawable
    }

    override fun setThemeColor(color: Int) {
        this.themeColor = color
    }

    override fun setKeyBackground(drawable: Drawable?) {
        this.keyBackground = drawable
        invalidate()
    }

    override fun setHorizontalGap(gap: Float) {
        this.horizontalGap = gap
        keyBuffer = null
        invalidate()
    }

    override fun setVerticalGap(gap: Float) {
        this.verticalGap = gap
        keyBuffer = null
        invalidate()
    }

    override fun setKeyTextSize(size: Float) {
        keyTextPaint.textSize = size
        invalidate()
    }

    override fun setKeyTextColor(color: Int) {
        keyTextPaint.color = color
        invalidate()
    }

    init {
        with(keyTextPaint) {
            isAntiAlias = true
            //设置文字中心对齐x坐标，不设置则从x坐标开始绘制
            textAlign = Align.CENTER
            color = Color.WHITE
            textSize = context.dp2px(20f).toFloat()
        }
        horizontalGap = context.dp2px(2f).toFloat()
        verticalGap = context.dp2px(4f).toFloat()
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        super.onTouchEvent(event)

        val currentX = event.getX(0)
        val currentY = event.getY(0)

        when (event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                clearDownKey()
                downKey = findKey(currentX, currentY)
                downKey?.pressed = true
                invalidate()
            }

            MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_CANCEL -> clearDownKey()

            MotionEvent.ACTION_UP -> {
                if (downKey != null) {
                    val upKey = findKey(currentX, currentY)
                    if (upKey != null && upKey == downKey) {
                        //防止onConfirmKey意外修改原始值
                        val originKey = upKey.copy()
                        onConfirmKey(upKey)
                        onKeyListener?.onKey(originKey)
                    }
                }
                clearDownKey()
            }

            MotionEvent.ACTION_MOVE -> {
                //手指时候滑出按键范围
                downKey?.let {
                    if (!inKeyBounds(currentX, currentY, it)) {
                        clearDownKey()
                    }
                }
            }
        }
        return true
    }

    private fun clearDownKey() {
        downKey?.let {
            it.pressed = false
            invalidate()
        }
        downKey = null
    }

    private fun inKeyBounds(eventX: Float, eventY: Float, key: Key): Boolean {
        return eventX >= key.left && eventX <= key.right && eventY >= key.top && eventY <= key.bottom
    }

    private fun findKey(eventX: Float, eventY: Float): Key? {
        val buffer = keyBuffer ?: return null
        for (key in buffer) {
            if (eventX >= key.left && eventX <= key.right && eventY >= key.top && eventY <= key.bottom) {
                return key
            }
        }
        return null
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (width == 0 || height == 0) {
            return
        }
        val keys = createKeyboard()
        for (key in keys) {
            onDrawKey(canvas, key, keyTextPaint)
        }
    }

    protected open fun onDrawKey(canvas: Canvas, key: Key, keyTextPaint: Paint) {
        val keyIconDrawable = keyIcons[key.code]
        val keyTxt = key.label

        drawKeyBackground(canvas, key, keyBackground)
        if (keyIconDrawable != null) {
            drawKeyIcon(canvas, key, keyIconDrawable)
        } else if (keyTxt?.isNotEmpty() == true) {
            drawKeyText(canvas, key)
        }
    }

    /**
     * 绘制按键图标
     */
    protected open fun drawKeyIcon(canvas: Canvas, key: Key, icon: Drawable) {
        val keyW = key.getWidth()
        val keyH = key.getHeight()

        //默认图标宽高
        var drawableWidth = keyW / 1.5f
        var drawableHeight = drawableWidth * (icon.intrinsicHeight.toFloat() / icon.intrinsicWidth)

        if (drawableHeight > keyH * 0.7f) {
            //显示图标过高,修正已高度标准计算
            drawableHeight = keyH * 0.7f
            drawableWidth = drawableHeight * (icon.intrinsicWidth.toFloat() / icon.intrinsicHeight)
        }

        val left = key.left + (keyW - drawableWidth) / 2f
        val top = key.top + (keyH - drawableHeight) / 2f
        val right = left + drawableWidth
        val bottom = top + drawableHeight

        icon.setBounds(left.toInt(), top.toInt(), right.toInt(), bottom.toInt())
        if (themeColor != Color.TRANSPARENT) {
            icon.setTint(themeColor)
        }
        icon.draw(canvas)
    }

    /**
     * 绘制按键背景
     */
    protected open fun drawKeyBackground(canvas: Canvas, key: Key, background: Drawable?) {
        background?.let {
            it.state = key.getDrawableState()
            it.setBounds(
                key.left.toInt(), key.top.toInt(), key.right.toInt(), key.bottom.toInt()
            )
            it.draw(canvas)
        }
    }

    /**
     * 绘制按键的文本
     */
    protected open fun drawKeyText(canvas: Canvas, key: Key) {
        val keyTxt = key.label
        if (keyTxt?.isNotEmpty() == true) {
            val metrics = keyTextPaint.fontMetrics
            val txtHeight = metrics.descent - metrics.ascent
            val keyHeight = getSuggestKeyHeight()
            val baselineY =
                max(0f, keyHeight - txtHeight) / 2f + abs(keyTextPaint.fontMetrics.ascent)

            val keyWidth = key.right - key.left
            canvas.drawText(keyTxt, key.left + keyWidth / 2f, key.top + baselineY, keyTextPaint)
        }
    }

    private fun createKeyboard(): List<Key> {
        if (keyBuffer?.isNotEmpty() == true) {
            return keyBuffer!!
        }
        val w = getSuggestKeyWidth()
        val h = getSuggestKeyHeight()
        return onCreateKeyboard(w, h).apply { keyBuffer = this }
    }

    /**
     * 获取键盘最多行数
     */
    protected abstract fun getMaxRowSize(): Int

    /**
     * 获取键盘最多列数
     */
    protected abstract fun getMaxColumnSize(): Int

    /**
     * 创建键盘布局
     * @param suggestKeyWidth 单个按键宽度
     * @param suggestKeyHeight 单个按键高度
     * @return 所有按键
     */
    protected abstract fun onCreateKeyboard(
        suggestKeyWidth: Float,
        suggestKeyHeight: Float
    ): List<Key>

    /**
     * 按键点击被确认时回调，可以在这里做一些内部操作
     */
    protected open fun onConfirmKey(key: Key) {
        //do nothing
    }

    override fun resetUI() {
        //do nothing
    }

    /**
     * 获取单个按键标准宽度
     */
    fun getSuggestKeyWidth(): Float {
        val column = getMaxColumnSize()
        if (column < 1) {
            throw IllegalArgumentException("getMaxColumnSize < 1")
        }
        return (width - paddingStart - paddingEnd - (column - 1) * horizontalGap) / column.toFloat()
    }

    /**
     * 获取单个按键标准高度
     */
    fun getSuggestKeyHeight(): Float {
        val row = getMaxRowSize()
        if (row < 1) {
            throw IllegalArgumentException("getMaxRowSize < 1")
        }
        return (height - paddingTop - paddingBottom - (row - 1) * verticalGap) / row.toFloat()
    }

}